/*
*    Phantom Test Driver v2.0 GUI Automation Software
*    Copyright (C) 2009  John C. Van Arsdall (john@phantomtest.com)
*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License along
*    with this program; if not, write to the Free Software Foundation, Inc.,
*    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/*
 * PTDTree.java
 *
 * Created on September 20, 2005, 6:42 PM
 *
 * To change this template, choose Tools | Options and locate the template under
 * the Source Creation and Management node. Right-click the template and choose
 * Open. You can then make changes to the template in the Source Editor.
 */

package ptd;

import javax.swing.tree.*;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionListener;
import javax.swing.event.TreeSelectionEvent;
import java.awt.Component;
import javax.swing.CellEditor;
import javax.swing.event.CellEditorListener;
import java.awt.event.*;
import java.util.EventObject;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import java.awt.event.MouseEvent;
import java.awt.Color;

import javax.swing.*;
import java.awt.Container;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import javax.swing.*;
import javax.swing.Icon;
import java.awt.Dimension;
//import java.awt.PopupMenu;
//import java.awt.MenuItem;

import javax.swing.JPopupMenu;
import javax.swing.JMenuItem;

import java.awt.event.MouseAdapter;

import java.io.FileInputStream;
import java.io.FileOutputStream;

import javax.swing.plaf.ComponentUI;
import ptd_suite_objects.*;

/**
 *
 * @author John
 */
public class PTDTree extends JTree{
    
    JPopupMenu SuitePopup;
    
    PTDMain pW;
    String SuiteFile;
    
    JMenuItem DeleteMenu;
    JMenuItem DeleteMenuParent;
    JMenuItem CutMenu;
    JMenuItem CutMenuParent;
    JMenu PasteMenu;
    JMenu PasteMenuParent;    
    JMenuItem PasteAfterMenu;
    JMenuItem PasteBeforeMenu;
    JMenuItem PasteIntoMenu;
    JMenuItem PasteAfterMenuParent;
    JMenuItem PasteBeforeMenuParent;
    JMenuItem PasteIntoMenuParent;
    
    
    JMenuItem EditMenu;
    JMenuItem EditMenuParent;
    JMenuItem RunMenu;
    JMenu InsertMenu;
    JMenu InsertMenuParent;
    private boolean bStaticUI;
    
    private boolean bModified;
    private DefaultMutableTreeNode m_CopyNodes;

    private boolean isEditable = true;
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public void setParent(PTDMain In){
        pW = In;
    }

    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    private void setModified(){
        bModified = true;
        PTDNodeInfo anInfo = (PTDNodeInfo)getRoot().getUserObject();
        
        anInfo.bModifiedFlag = bModified;
        DefaultTreeModel model = (DefaultTreeModel)getModel();
        model.nodeChanged(getRoot());  
    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    private void clearModified(){
        bModified = false;
        PTDNodeInfo anInfo = (PTDNodeInfo)getRoot().getUserObject();
        anInfo.bModifiedFlag = bModified;
        DefaultTreeModel model = (DefaultTreeModel)getModel();
        model.nodeChanged(getRoot());  

    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public boolean getModified(){
        return bModified;
    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public boolean closeMe(){
         if(bModified){
            int iSel = PTDMain.saveConfirm("The suite has been modified.  Do you want to save it?", "Suite Modified");
            if(iSel == JOptionPane.CANCEL_OPTION){
                return false;
            }else if(iSel == JOptionPane.YES_OPTION){
                if(SuiteFile.length() > 0){
                    saveToFile(this.SuiteFile);
                }else{
                    return pW.performSuiteSaveAs();
                }
            }else{
                clearModified(); // Have to do this, else Suite New, Suite Open will have modified flag set
                return true;
            }
        }
        return true;
    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public void GenerateMenus(){
        SuitePopup = new JPopupMenu();
        EditMenu = new JMenuItem("Edit");
        EditMenu.setMnemonic('E');
        EditMenu.setToolTipText("Edit the script associated with the testcase");
        SuitePopup.add(EditMenu);

        RunMenu = new JMenuItem("Run");
        RunMenu.setMnemonic('R');
        RunMenu.setToolTipText("Run the selected suite or testcase");
        SuitePopup.add(RunMenu);

        JMenuItem CopyMenu = new JMenuItem("Copy");
        CopyMenu.setMnemonic('C');
        CopyMenu.setToolTipText("Copy the selected suite or testcase");
        SuitePopup.add(CopyMenu);
        
        CutMenu = new JMenuItem("Cut");
        CutMenu.setMnemonic('u');
        CutMenu.setToolTipText("Cut the selected suite or testcase");
        SuitePopup.add(CutMenu);
        
        PasteMenu = new JMenu("Paste");
        PasteMenu.setToolTipText("Paste the suite or testcase in the copy buffer");
        PasteMenu.setMnemonic('P');
        SuitePopup.add(PasteMenu);
        

        PasteIntoMenu = new JMenuItem("Paste Into");
        PasteIntoMenu.setMnemonic('I');
        PasteIntoMenu.setToolTipText("Pastes a suite or testcase as a child to the selected item");
        PasteMenu.add(PasteIntoMenu);

        PasteBeforeMenu = new JMenuItem("Paste Before");
        PasteBeforeMenu.setMnemonic('B');
        PasteBeforeMenu.setToolTipText("Pastes a suite or testcase before the selected item");
        PasteMenu.add(PasteBeforeMenu);

        PasteAfterMenu = new JMenuItem("Paste After");
        PasteAfterMenu.setMnemonic('A');
        PasteAfterMenu.setToolTipText("Pastes a suite or testcase after the selected item");
        PasteMenu.add(PasteAfterMenu);
        
        
        DeleteMenu = new JMenuItem("Delete");
        DeleteMenu.setMnemonic('D');
        DeleteMenu.setToolTipText("Deletes the selected suite or testcase");
        SuitePopup.add(DeleteMenu);
        
        InsertMenu = new JMenu("Insert");
        InsertMenu.setMnemonic('I');
        InsertMenu.setToolTipText("Inserts a new suite or testcase as a child of the selected item");
        SuitePopup.add(InsertMenu);

        JMenuItem InsertSuiteMenu = new JMenuItem("Suite");
        InsertSuiteMenu.setMnemonic('S');
        InsertSuiteMenu.setToolTipText("Inserts a new suite");
        InsertMenu.add(InsertSuiteMenu);

        JMenuItem InsertTestcaseMenu = new JMenuItem("Testcase");
        InsertTestcaseMenu.setMnemonic('T');
        InsertTestcaseMenu.setToolTipText("Inserts a new testcase");
        InsertMenu.add(InsertTestcaseMenu);

        InsertMenu.setFont(new java.awt.Font("Arial", 0, 12));
        InsertTestcaseMenu.setFont(new java.awt.Font("Arial", 0, 12));
        InsertSuiteMenu.setFont(new java.awt.Font("Arial", 0, 12));
        DeleteMenu.setFont(new java.awt.Font("Arial", 0, 12));
        PasteMenu.setFont(new java.awt.Font("Arial", 0, 12));
        PasteAfterMenu.setFont(new java.awt.Font("Arial", 0, 12));
        PasteBeforeMenu.setFont(new java.awt.Font("Arial", 0, 12));
        PasteIntoMenu.setFont(new java.awt.Font("Arial", 0, 12));
        RunMenu.setFont(new java.awt.Font("Arial", 0, 12));
        CutMenu.setFont(new java.awt.Font("Arial", 0, 12));
        CopyMenu.setFont(new java.awt.Font("Arial", 0, 12));
        EditMenu.setFont(new java.awt.Font("Arial", 0, 12));
        
        EditMenu.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                EditMenuActionPerformed(evt);
            }
        });

        CopyMenu.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                CopyMenuActionPerformed(evt);
            }
        });
        
        CutMenu.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                CutMenuActionPerformed(evt);
            }
        });
        
        //PasteMenu.addActionListener(new java.awt.event.ActionListener() {
        //    public void actionPerformed(java.awt.event.ActionEvent evt) {
        //        PasteMenuActionPerformed(evt);
        //    }
        //});

        PasteIntoMenu.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                PasteIntoMenuActionPerformed(evt);
            }
        });        
        
        PasteAfterMenu.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                PasteAfterMenuActionPerformed(evt);
            }
        });

        PasteBeforeMenu.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                PasteBeforeMenuActionPerformed(evt);
            }
        });

        
        DeleteMenu.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                DeleteMenuActionPerformed(evt);
            }
        });

        RunMenu.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                RunMenuActionPerformed(evt);
            }
        });

        
        InsertTestcaseMenu.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                InsertTestcaseMenuActionPerformed(evt);
            }
        });

        InsertSuiteMenu.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                InsertSuiteMenuActionPerformed(evt);
            }
        });
    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public void saveToFile(String fileName){
        try{
            SuiteFile = fileName;
            FileOutputStream saveFile = new FileOutputStream(fileName);
            saveTreeNode(getRoot(), saveFile);            
            saveFile.close();
            clearModified();
        }catch (Exception e){
            Main.debug_out(e.getMessage());
        }
    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public void loadFromFile(String fileName){
        try{
            FileInputStream loadFile = new FileInputStream(fileName);
            DefaultMutableTreeNode newRoot = loadTreeNode(loadFile);
            if(newRoot != null){
                setSelectionPath(null);
                ((DefaultTreeModel)treeModel).setRoot(newRoot);
            }else{
                Main.debug_out("Could not load Suite");
            }
            loadFile.close();
            //validate();
            repaint();
            SuiteFile = fileName;
            
        }catch (Exception e){
            Main.debug_out(e.getMessage());
        }
    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public void newSuite(){
        PTDNodeInfo anInfo = new PTDNodeInfo();
        SuiteBase aSuite = new SuiteBase();
        aSuite.myName = "Root";
        this.SuiteFile = "";
        anInfo.init(aSuite);
        DefaultMutableTreeNode aNode = new DefaultMutableTreeNode(anInfo, true);
        
        setSelectionPath(null);
        ((DefaultTreeModel)treeModel).setRoot(aNode);
        
        DefaultTreeModel model = (DefaultTreeModel)getModel();        
        model.nodeChanged(getRoot()); 
    }

    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public SuiteRunBase generateSet(DefaultMutableTreeNode inNode){
        SuiteRunBase Ret;
        PTDNodeInfo anInfo = (PTDNodeInfo)inNode.getUserObject();
        Ret = new SuiteRunBase(anInfo.myData);
        int ChildCount = inNode.getChildCount();
        if(ChildCount > 0){
            int ctr;
            for(ctr = 0; ctr < ChildCount; ctr++){
                DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)inNode.getChildAt(ctr);
                PTDNodeInfo childInfo = (PTDNodeInfo)childNode.getUserObject();
                if(childInfo.myData.myRunSelectState > 0){
                    Ret.addNode(generateSet(childNode));
                }
            }
        }
        return Ret;
    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public int runNode(DefaultMutableTreeNode inNode){
        
        SuiteRunBase aSet = generateSet(inNode);
        PTDHTMLPane aPane = pW.getNewHTMLPane("Suite: "+aSet.getObject().myName);
        
        PTDSuiteThread aSuiteThread = new PTDSuiteThread();
        aSuiteThread.init(aSet, aPane, aPane);
        aSuiteThread.start();
        return 0;
    }

    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public int runNodeRemote(DefaultMutableTreeNode inNode, PTDRemoteMachine Machine){
        
        SuiteRunBase aSet = generateSet(inNode);
        PTDHTMLPane aPane = pW.getNewHTMLPane("[" + Machine.myName + "] "+aSet.getObject().myName);
        Machine.runState = true;
        aPane.remoteMachine = Machine;
        PTDSuiteThread aSuiteThread = new PTDSuiteThread();
        aSuiteThread.initRemote(aSet, aPane, aPane, Machine);
        aSuiteThread.start();
        //aPane.remoteMachine = null;
        return 0;
    }    
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public DefaultMutableTreeNode loadTreeNode(FileInputStream In){
        SuiteBase NewItem;
        boolean bAllowChildren = false;
        int Type = SuiteBase.loadType(In);
        DefaultMutableTreeNode Ret = null;
        if(Type == SuiteBase.SUITE_TYPE){
            bAllowChildren = true;
            NewItem = new SuiteBase();
        }else{
            bAllowChildren = false;
            NewItem = (SuiteBase)(new SuiteTestcase());
        }
        
        if(NewItem.load(In) == 0){
            return null;
        }
        PTDNodeInfo newInfo = new PTDNodeInfo();
        newInfo.init(NewItem);
        Ret = new DefaultMutableTreeNode(newInfo, bAllowChildren);
        
        int ctr;
        for(ctr = 0; ctr < NewItem.myChildrenCount; ctr++){
            DefaultMutableTreeNode aChild = loadTreeNode(In);
            if(aChild != null){
                Ret.insert(aChild, ctr);
            }else{
                return null;
            }
        }
        
        return Ret;
    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public boolean saveTreeNode(DefaultMutableTreeNode Input, FileOutputStream Out){
        boolean bRet = false;
       PTDNodeInfo Info = (PTDNodeInfo)Input.getUserObject();
       int cnt = Input.getChildCount();
       // Temporary
       Info.myData.myChildrenCount = cnt;
       if(Info.myData.save(Out) == 0){  //save(Out);
             return false;
       }
       
       int ctr;
       for ( ctr = 0; ctr < cnt; ctr++){
         DefaultMutableTreeNode aChild = (DefaultMutableTreeNode)Input.getChildAt(ctr);
         bRet = saveTreeNode(aChild, Out);
         if(bRet == false){
             return bRet;
         }
       }
       return true;
    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public DefaultMutableTreeNode getRoot(){
        return (DefaultMutableTreeNode)((DefaultTreeModel)treeModel).getRoot();
    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public void PopulateMenu(JMenuItem InMenu, JPopupMenu Source){
        int cnt = Source.getComponentCount();
        int i;
        for(i = 0; i < cnt; i++){
            
            // This should work for both JMenu and JMenuItem
            JMenuItem a = (JMenuItem)Source.getComponent(i);
            Source.insert(a, i);
            
            MenuElement[] elems = a.getSubElements();
            int len = elems.length;
            
            if(len > 0){
                JMenu b;
                if(a == InsertMenu){
                    InsertMenuParent = new JMenu(a.getText());
                    b = InsertMenuParent;
                }else if(a == PasteMenu){
                    PasteMenuParent = new JMenu(a.getText());
                    b = PasteMenuParent;                    
                }else{
                    b = new JMenu(a.getText());
                }
         
                b.setToolTipText(a.getToolTipText());
                b.setMnemonic(a.getMnemonic());
                PopulateMenu(b, (JPopupMenu)elems[0]);
                b.setFont(new java.awt.Font("Arial", 0, 12));
                
                InMenu.add(b);
            }else{

                JMenuItem b;                
                if(a == DeleteMenu){
                    DeleteMenuParent = new JMenuItem(a.getText());
                    b = DeleteMenuParent;
                }else if(a == CutMenu){
                    CutMenuParent = new JMenuItem(a.getText());
                    b = CutMenuParent;
                //}else if(a == PasteMenu){
                //    PasteMenuParent = new JMenuItem(a.getText());
                //    b = PasteMenuParent;                    
                }else if(a == PasteIntoMenu){
                    PasteIntoMenuParent = new JMenuItem(a.getText());
                    b = PasteIntoMenuParent;                                        
                }else if(a == PasteAfterMenu){
                    PasteAfterMenuParent = new JMenuItem(a.getText());
                    b = PasteAfterMenuParent;                                        
                }else if(a == PasteBeforeMenu){
                    PasteBeforeMenuParent = new JMenuItem(a.getText());
                    b = PasteBeforeMenuParent;                                        
                }else if(a == EditMenu){
                    EditMenuParent = new JMenuItem(a.getText());
                    b = EditMenuParent;                    
                }else{
                    b = new JMenuItem(a.getText());
                }
                
                b.setFont(new java.awt.Font("Arial", 0, 12));
                b.setToolTipText(a.getToolTipText());
                b.setMnemonic(a.getMnemonic());
                java.awt.event.ActionListener[] l = a.getActionListeners();
                len = l.length;
                while(len > 0){
                    b.addActionListener(l[len-1]);
                    len = len - 1;
                }
                InMenu.add(b);
            }
        }
    }
   
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    private void EditMenuActionPerformed(java.awt.event.ActionEvent evt) {                                             
        
        PTDNodeInfo anInfo = getSelectedNode();
        if(anInfo == null){
            return;
        }
        SuiteBase aBase = anInfo.myData;
        if(aBase.myType == aBase.TESTCASE_TYPE){
            pW.addScriptPane(((SuiteTestcase)aBase).myFile);
        }
        Main.debug_out("Suite Edit Selected");
    }

    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    private void CopyMenuActionPerformed(java.awt.event.ActionEvent evt) {                                             
        DefaultMutableTreeNode aNode = getSelectedTreeNode();
        CopyNode(aNode, null);
        Main.debug_out("Suite Copy Selected");
    }

    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    private void CutMenuActionPerformed(java.awt.event.ActionEvent evt) {                                             
        DefaultMutableTreeNode aNode = getSelectedTreeNode();
        CopyNode(aNode, null);
        DeleteNode(aNode);
        Main.debug_out("Suite Cut Selected");
    }

    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    private void PasteIntoMenuActionPerformed(java.awt.event.ActionEvent evt) {                                             
        DefaultMutableTreeNode aNode = getSelectedTreeNode();
        
        DefaultMutableTreeNode tempNode = m_CopyNodes;
        CopyNode(tempNode, null); // This in effect copies m_CopyNodes into a new copy
        PasteNode(tempNode, aNode, 0);
        Main.debug_out("Suite Paste After Selected");
    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    private void PasteAfterMenuActionPerformed(java.awt.event.ActionEvent evt) {                                             
        DefaultMutableTreeNode aNode = getSelectedTreeNode();
        
        DefaultMutableTreeNode tempNode = m_CopyNodes;
        CopyNode(tempNode, null); // This in effect copies m_CopyNodes into a new copy
        PasteNode(tempNode, aNode, 1);
        Main.debug_out("Suite Paste After Selected");
    }

    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    private void PasteBeforeMenuActionPerformed(java.awt.event.ActionEvent evt) {                                             
        DefaultMutableTreeNode aNode = getSelectedTreeNode();
        DefaultMutableTreeNode tempNode = m_CopyNodes;
        CopyNode(tempNode, null); // This in effect copies m_CopyNodes into a new copy
        PasteNode(tempNode, aNode, 2);
        Main.debug_out("Suite Paste Before Selected");
    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    private void DeleteMenuActionPerformed(java.awt.event.ActionEvent evt) {                                             
        DefaultMutableTreeNode aNode = getSelectedTreeNode();
        DeleteNode(aNode);
        Main.debug_out("Suite Delete Selected");
    }

    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    private void DeleteNode(DefaultMutableTreeNode aNode){
        try{
            // Remove the selection
            setSelectionRow(0);
            ((DefaultTreeModel)treeModel).removeNodeFromParent(aNode);
        }catch(Exception e){
            
        }
    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    private void CopyNode(DefaultMutableTreeNode aNode, DefaultMutableTreeNode aParent){
        if(aNode == null){
            return;
        }
        if(aParent == null){
            
            PTDNodeInfo anInfo = (PTDNodeInfo)aNode.getUserObject();
            PTDNodeInfo newInfo = new PTDNodeInfo();
            
            SuiteBase aBase = new SuiteBase();
            newInfo.init(anInfo.myData);        
            boolean AllowChildren = false;
            if(anInfo.myData.myType == SuiteBase.SUITE_TYPE){
                AllowChildren = true;
            }
            m_CopyNodes = new DefaultMutableTreeNode(newInfo, AllowChildren); //(DefaultMutableTreeNode)aNode.clone();
            aParent = m_CopyNodes;
        }
        
        int i = 0;
        DefaultMutableTreeNode aChild; //= (DefaultMutableTreeNode)aNode.clone();
        for(i = 0; i < aNode.getChildCount(); i++){
            aChild = (DefaultMutableTreeNode)aNode.getChildAt(i);
            PTDNodeInfo anInfo = (PTDNodeInfo)aChild.getUserObject();
            PTDNodeInfo newInfo = new PTDNodeInfo();
            
            SuiteBase aBase = new SuiteBase();
            newInfo.init(anInfo.myData);        
            boolean AllowChildren = false;
            if(anInfo.myData.myType == SuiteBase.SUITE_TYPE){
                AllowChildren = true;
            }
            aChild = new DefaultMutableTreeNode(newInfo, AllowChildren);

            CopyNode((DefaultMutableTreeNode)aNode.getChildAt(i), aChild);
            aParent.add(aChild);
        }   
        return;
    }

    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    private void PasteNode(DefaultMutableTreeNode aNode, DefaultMutableTreeNode aParent, int iPosition){
        if(aParent == null){
            return;
        }
        if(aNode == null){
            return;
        }   
        DefaultMutableTreeNode pParent = (DefaultMutableTreeNode)aParent.getParent();
        int index = 0;
        if(pParent != null){
            index = pParent.getIndex(aParent);
        }
        
        if(iPosition == 0){
            // Paste Into
            ((DefaultTreeModel)treeModel).insertNodeInto(aNode, aParent, 0);
        }else if(iPosition == 1){
            // Paste After
            if(pParent != null){
                index = index + 1;
                ((DefaultTreeModel)treeModel).insertNodeInto(aNode, pParent, index);
                
            }
        }else{
            // Paste Before
            if(pParent != null){
                ((DefaultTreeModel)treeModel).insertNodeInto(aNode, pParent, index);
            }
        }
        if(pParent != null){
            ((DefaultTreeModel)treeModel).nodeStructureChanged(pParent);
        }
        setModified();        
        return;
    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    private void RunMenuActionPerformed(java.awt.event.ActionEvent evt) {                                             
        //Run();
        Main.getMainFrame().runSuite();
    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public void Run(){
        DefaultMutableTreeNode aNode = getSelectedTreeNode();
        if(aNode == null){
            aNode = getRoot();
            if(aNode == null){
                return;
            }
        }
        runNode(aNode);
    }

    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public void RunRemote(PTDRemoteMachine Machine){
        DefaultMutableTreeNode aNode = getSelectedTreeNode();
        if(aNode == null){
            aNode = getRoot();
            if(aNode == null){
                return;
            }
        }
        pW.addRemoteMachine(Machine);
        runNodeRemote(aNode, Machine);
    }    
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    private void InsertTestcaseMenuActionPerformed(java.awt.event.ActionEvent evt) {                                             
        SuiteTestcase aTestcase = new SuiteTestcase();
        aTestcase.myName = "New Testcase";
        pW.setProps(aTestcase);
        insertNewItem(pW.ActiveProps.getNewData(), 0);
    }

    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    private void InsertSuiteMenuActionPerformed(java.awt.event.ActionEvent evt) {                                             
        
        SuiteBase aBase = new SuiteBase();
        aBase.myName = "New Suite";
        pW.setProps(aBase);
        insertNewItem(pW.ActiveProps.getNewData(), 0);
    }

    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    private PTDNodeInfo getSelectedNode(){
        TreePath SelPath = getSelectionPath();
        if(SelPath == null){
            return null;
        }
        DefaultMutableTreeNode SelNode = (DefaultMutableTreeNode)SelPath.getLastPathComponent();
        if(SelNode == null){
            return null;
        }
        
        PTDNodeInfo Ret = (PTDNodeInfo)SelNode.getUserObject();
        return Ret;
    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    private DefaultMutableTreeNode getSelectedTreeNode(){
        TreePath SelPath = getSelectionPath();
        
        if(SelPath == null){
            return null;
        }
        DefaultMutableTreeNode SelNode = (DefaultMutableTreeNode)SelPath.getLastPathComponent();
        if(SelNode == null){
            return null;
        }
        return SelNode;
    }

    // MYKEYLISTENER ////////////////////////////////
    class MyKeyListener extends java.awt.event.KeyAdapter{
        private PTDTree pW;
        
        // -----------------------------------------------------
        public MyKeyListener(PTDTree InTree){
            pW = InTree;
        }
        
        // -----------------------------------------------------
        public void keyTyped(KeyEvent e) {
            Main.debug_out("Key Typed");
        }
        
        // -----------------------------------------------------
        public void keyPressed(KeyEvent e) {
            if(e.getKeyCode() == e.VK_ENTER){                
            }else if(e.getKeyCode() == e.VK_UP){
            }else if(e.getKeyCode() == e.VK_DOWN){
            }else if(e.getKeyCode() == e.VK_LEFT){
            }else if(e.getKeyCode() == e.VK_HOME){
            }else if(e.getKeyCode() == e.VK_BACK_SPACE){
            }
        }

        // -----------------------------------------------------
        public void keyReleased(KeyEvent e) {
        }
    }
    // END MYKEYLISTENER ////////////////////////////////
    
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public PTDTree(boolean bEditable) {
        
        isEditable = bEditable;
        bStaticUI = true;
        
        if(isEditable){
            this.addMouseListener(new PTDTreeMouseListener());
        }
        this.addKeyListener(new MyKeyListener(this));
        
        PTDTreeCell CellEdit = new PTDTreeCell();
        PTDTreeRender CellRender = new PTDTreeRender();
        
        this.showsRootHandles = false;
        
        SuiteFile = "";
        m_CopyNodes = null;
        
        if(isEditable){
            setEditable(true);  // Need for check-boxes
                                // However, this may break the main tree display after a remote run
        }else{
            setEditable(false);
        }
        
        this.setEnabled(true);
        this.setVisible(true);
 
        setCellRenderer(CellRender);
        setCellEditor(CellEdit);
        
        if(isEditable){
            GenerateMenus();
            add(SuitePopup);
        }
        
        PTDNodeInfo anInfo = new PTDNodeInfo();
        SuiteBase aSuite = new SuiteBase();
        aSuite.myName = "Root";
        anInfo.init(aSuite);
        DefaultMutableTreeNode aNode = new DefaultMutableTreeNode(anInfo, true);
        CellRender.isInitialized = true;
        
        setSelectionPath(null);
        ((DefaultTreeModel)treeModel).setRoot(aNode);
        
        getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);

        if(isEditable){
            addTreeSelectionListener(new TreeSelectionListener(){
               public void valueChanged(TreeSelectionEvent e){
                   DefaultMutableTreeNode node = (DefaultMutableTreeNode)getLastSelectedPathComponent();

                   DefaultTreeModel model = (DefaultTreeModel)getModel();
                   if(e.getOldLeadSelectionPath() != null){
                     TreeNode oldNode = (TreeNode)e.getOldLeadSelectionPath().getLastPathComponent();
                     if(oldNode != null){
                        model.nodeChanged(oldNode);
                     }
                   }

                   if(node != null){
                       selectItem(node);
                   }else{
                       Main.debug_out("NULL Selection");
                   }
               } 
            });
        }
    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public void insertNewItem(SuiteBase InBase, int Location){
        
        boolean AllowChildren = false;
        
        PTDNodeInfo anInfo = new PTDNodeInfo();
        anInfo.init(InBase);
        
        if(InBase.myType == SuiteBase.SUITE_TYPE){
            AllowChildren = true;
        }
        
        DefaultMutableTreeNode newItem = new DefaultMutableTreeNode(anInfo, AllowChildren);
        PTDNodeInfo selNode = getSelectedNode();

        DefaultMutableTreeNode selItem = getSelectedTreeNode();
        
        if((selItem != null) && (Location == 0)){
            int cnt = selItem.getChildCount();
            ((DefaultTreeModel)treeModel).insertNodeInto(newItem, selItem, cnt);
            scrollPathToVisible(new TreePath(newItem.getPath()));
        }else if(selNode != null){
            
        }else{
            
        }
        setModified();
        setSelectionPath(new TreePath(newItem.getPath()));
    }
    
    public void updateSelectedItem(){
        DefaultMutableTreeNode SelNode = getSelectedTreeNode();
        if(SelNode == null){
            return;
        }
        
        TreePath selPath = new TreePath(SelNode.getPath());
        clearSelection();
        setSelectionPath(selPath);
        DefaultTreeModel model = (DefaultTreeModel)getModel();
        model.nodeChanged(SelNode);  
    }

    public void resetSelectedItem(){
        DefaultMutableTreeNode SelNode = getSelectedTreeNode();
        if(SelNode == null){
            return;
        }
        PTDNodeInfo anInfo = (PTDNodeInfo)SelNode.getUserObject();
        pW.setProps(anInfo.myData);
    }
    
    public void selectItem(DefaultMutableTreeNode selNode){
         PTDNodeInfo anInfo = (PTDNodeInfo)selNode.getUserObject();
         if(selNode != null){                
             SuiteBase oldBase = pW.getProps();
             if(oldBase != null){
                 if(oldBase.getModified()){
                    setModified();
                 }
             }
             // -- SUITE MODIFIED --
             //setModified(pW.getSuiteModified());
             //updateUI();  For some reason, this causes the UI to *not* be updated
             pW.setProps(anInfo.myData);
         }

         if(anInfo.myData.myType == SuiteBase.SUITE_TYPE){
            EditMenu.setEnabled(false);
            
            InsertMenu.setEnabled(true);
            EditMenuParent.setEnabled(false);
            
            PasteMenu.setEnabled(true);
            PasteMenuParent.setEnabled(true);
            PasteIntoMenu.setEnabled(true);
            PasteIntoMenuParent.setEnabled(true);
            PasteAfterMenu.setEnabled(true);
            PasteAfterMenuParent.setEnabled(true);
            PasteBeforeMenu.setEnabled(true);
            PasteBeforeMenuParent.setEnabled(true);
            
            InsertMenuParent.setEnabled(true);                
        }else{
            EditMenu.setEnabled(true);
            
            InsertMenu.setEnabled(false);
            EditMenuParent.setEnabled(true);
            
            PasteMenu.setEnabled(true);
            PasteMenuParent.setEnabled(true);
            PasteIntoMenu.setEnabled(false);
            PasteIntoMenuParent.setEnabled(false);
            PasteAfterMenu.setEnabled(true);
            PasteAfterMenuParent.setEnabled(true);
            PasteBeforeMenu.setEnabled(true);
            PasteBeforeMenuParent.setEnabled(true);
            
            InsertMenuParent.setEnabled(false);
        }

        if(m_CopyNodes == null){
            PasteMenuParent.setEnabled(false);
            PasteMenu.setEnabled(false);
        }

        if(selNode == getRoot()){
            DeleteMenu.setEnabled(false);
            CutMenu.setEnabled(false);
            DeleteMenuParent.setEnabled(false);
            CutMenuParent.setEnabled(false);
            
            PasteIntoMenu.setEnabled(true);
            PasteIntoMenuParent.setEnabled(true);
            PasteAfterMenu.setEnabled(false);
            PasteAfterMenuParent.setEnabled(false);
            PasteBeforeMenu.setEnabled(false);
            PasteBeforeMenuParent.setEnabled(false);
        }else{
            DeleteMenu.setEnabled(true);
            CutMenu.setEnabled(true);
            DeleteMenuParent.setEnabled(true);
            CutMenuParent.setEnabled(true);
        }

    }
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    public class PTDTreeMouseListener extends MouseAdapter{
    /*            
    Use the getPathForLocation method first on the JTree object to get the TreePath 
    TreePath selPath = jtree.getPathForLocation(e.getX(), e.getY()); 
    (Here, e is the MouseEvent object) 

    Next use the getLastPathComponent method to get the DefaultMutableTreeNode 
    DefaultMutableTreeNode selectedNode = null;
    try { selectedNode = (DefaultMutableTreeNode)selPath.getLastPathComponent(); }
    catch (Exception E){} 
    selectedNode returns null if the event did not occur on an object of type DefaultMutableTreeNode
    */            

        // -----------------------------------------------------
        public void mousePressed(MouseEvent e) {
            // Required to allow keyboard navigation when a tree item is selected.
            requestFocus();
            
            if(pW == null){
                return;
            }
                        
            TreePath selPath = getPathForLocation(e.getX(), e.getY());
            if(selPath == null){
                return;
            }
            DefaultMutableTreeNode selNode = (DefaultMutableTreeNode)selPath.getLastPathComponent();
            if(selNode == null){
                return;
            }
            
            selectItem(selNode);
            if(e.getButton() == e.BUTTON3){
                
                if(selPath == null){
                    return;
                }
                if(selNode != null){
                    Component c = e.getComponent();
                    if(c == null){
                        //Main.debug_out("Popup-event");
                    }else{
                        SuitePopup.show(c, e.getX(), e.getY());
                    }
                }
            } 
        }
        // -----------------------------------------------------
        //public void mouseReleased(MouseEvent e) {
        //    if (e.isPopupTrigger()) {
        //    }
        //}
    }
    
   public void updateUI() {
       super.updateUI();
   }

    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    // PTDTREECELL ///////////////////////////////////////
    // Used for when the cell is editable
    public class PTDTreeCell implements TreeCellEditor{
        
        // -----------------------------------------------------
        public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row){
            DefaultMutableTreeNode node =  (DefaultMutableTreeNode)value;

            if(node.isLeaf() || node.getAllowsChildren())
            {
                PTDNodeInfo anInfo = (PTDNodeInfo)node.getUserObject();
                anInfo.myFrame.update(anInfo);
                if(anInfo == null){
                    return new JLabel("Suite Error");
                }
                anInfo.myFrame.setSelectState(true);
                return anInfo.myFrame;
            }
            else
            {
                JLabel Label = new JLabel("Suite Error");
                return Label;
            }
        }
        
        // -----------------------------------------------------
        public void addCellEditorListener(CellEditorListener l){
            
        }

        // -----------------------------------------------------
        public void removeCellEditorListener(CellEditorListener l){
            
        }
        
        // -----------------------------------------------------
        public Object getCellEditorValue(){
            return this;
        }
        
        // -----------------------------------------------------
        public void cancelCellEditing(){
        }
 
        // -----------------------------------------------------
        public boolean stopCellEditing(){
            return true;
        }
 
        // -----------------------------------------------------
        public boolean isCellEditable(EventObject isEditableEvt){
            if (isEditableEvt instanceof MouseEvent){
                MouseEvent me = (MouseEvent) isEditableEvt;
                if (me.getClickCount() == 1){
                    return true;
                }
            }
            return false;
        }
        
        // -----------------------------------------------------
        public boolean shouldSelectCell(EventObject selectCell){
            return true;
        }
    }
    // END PTDTreeCell ///////////////////////////////////////
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    // PTDTreeRender ///////////////////////////////////////
    // Used to render the tree item under non-edit circumstances
    public class PTDTreeRender extends DefaultTreeCellRenderer{
        
        public boolean isInitialized;
        
        // -----------------------------------------------------
        public PTDTreeRender(){
            isInitialized = false;
        }
 
        // -----------------------------------------------------
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus){
            DefaultMutableTreeNode node =  (DefaultMutableTreeNode)value;
            if(isInitialized == false){
                return new JLabel("Ugh");
            }
            
            if(node.isLeaf() || node.getAllowsChildren()){
                PTDNodeInfo anInfo = (PTDNodeInfo)node.getUserObject();                
                anInfo.myFrame.update(anInfo);
                if(anInfo == null){
                    return new JLabel("Suite Error");
                }
                if(selected){
                    anInfo.myFrame.setSelectState(true);
                }else{
                    anInfo.myFrame.setSelectState(false);
                }
                return anInfo.myFrame;
            }else{
                JLabel Label = new JLabel("Error");
                return Label;
            }
        }
    }
    // END PTDTreeRender ///////////////////////////////////////
    
    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    // PTDNodeInfo ///////////////////////////////////////
    public class PTDNodeInfo extends Object{
        public SuiteBase myData;
        public PTDTreeFrame myFrame;
        public boolean bModifiedFlag;
        
        // -----------------------------------------------------
        public PTDNodeInfo(){
            myData = null;
            myFrame = null;
            bModifiedFlag = false;
        }
        
        // -----------------------------------------------------
        public void init(SuiteBase newData){
            myData = newData.duplicate();
            myFrame = new PTDTreeFrame(myData);
            myFrame.update(this);
        }
        
    }
    // END PTDNodeInfo ///////////////////////////////////////

    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    // PTDTreeFrame ///////////////////////////////////////
    public class PTDTreeFrame extends JPanel{
        public JCheckBox myCheck;
        public JLabel myLabel;
        public Icon myIcon;
        public SuiteBase Parent;
        
        // -----------------------------------------------------
        public PTDTreeFrame(SuiteBase inParent){
            
            Parent = inParent;
            
            myCheck = new JCheckBox();
            myLabel = new JLabel();
            if(inParent.myType == inParent.SUITE_TYPE){
                myIcon = PTDImageLoader.getImageIcon(PTDImageLoader.IMG_SUITE_ICON);
            }else{
                myIcon = PTDImageLoader.getImageIcon(PTDImageLoader.IMG_TESTCASE_ICON);
            }
            myLabel.setIcon(myIcon);
            
            setLayout(new java.awt.GridBagLayout());

            myCheck.setBackground(Color.WHITE);
            myLabel.setBackground(Color.WHITE);
            this.setBackground(Color.WHITE);
            add(myCheck);
            add(myLabel);
    
            myLabel.setFont(new java.awt.Font("Arial", 0, 12));
            
            myCheck.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    SelectCheckActionPerformed(evt);
                }
            });

        }

        // -----------------------------------------------------
        private void SelectCheckActionPerformed(java.awt.event.ActionEvent evt) {                                                
            Parent.myRunSelectState = getCheckState();
        }                                               
        
        // -----------------------------------------------------
        public void setSelectState(boolean selState){
            
            if(selState){
                Color LtBlue = new Color(200, 220, 255);
                myCheck.setBackground(LtBlue);
                myLabel.setBackground(LtBlue);
                this.setBackground(LtBlue);
            }else{
                myCheck.setBackground(Color.WHITE);
                myLabel.setBackground(Color.WHITE);
                this.setBackground(Color.WHITE);
            }
        }

        // -----------------------------------------------------
        public void setCheckState(SuiteBase aBase){
            if(aBase.myRunSelectState > 0){
                myCheck.setSelected(true);
            }else{
                myCheck.setSelected(false);
            }
        }
        
        // -----------------------------------------------------
        public int getCheckState(){
            if(myCheck.isSelected()){
                return 1;
            }else{
                return 0;
            }
        }
        
        // -----------------------------------------------------
        public void update(PTDNodeInfo anInfo){
            SuiteBase aBase = anInfo.myData;
            //String oldLabel = myLabel.getText();  
            if(anInfo.bModifiedFlag){
                myLabel.setText(aBase.myName + " *");
            }else{
                myLabel.setText(aBase.myName);
            }            
            
            setCheckState(aBase);
        }
    }
    // END PTDTreeFrame ///////////////////////////////////////
}
